package com.cll.aspect;


import com.cll.annotation.AutoFill;
import com.cll.constant.AutoFillConstant;
import com.cll.context.BaseContext;
import com.cll.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * AOP切面思维 自定义切面，实现公共字段自动填充处理逻辑
 */
@Aspect       //把当前类标识为一个切面供容器读取
@Slf4j        //输出日志
@Component    //设置该类为spring管理的bean
public class AutoFillAspect {

    /**
     * 切入点表达式：扫描mapper包下所有类与方法 和 使用注解@AutoFill 的方法进行增强
     */
    @Pointcut("execution(* com.cll.mapper.*.*(..)) && @annotation(com.cll.annotation.AutoFill)")
    public void pt(){}

    /**
     * 前置通知
     */
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info("公共字段开始填充：{}",joinPoint);

        //目标：对指定的增强方法获取参数对象，填充公共字段

        //1.通过joinPoint获取方法签名对象（原始方法）
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        //2.通过方法签名对象获取@AutoFill注解对象
        AutoFill autoFill= methodSignature.getMethod().getAnnotation(AutoFill.class);
        //3.获取@AutoFill注解对象的值，便于后面对insert或update做不同的处理
        OperationType operationType = autoFill.value();
        //4.joinPoint获取原始方法的参数数组
        Object[] args = joinPoint.getArgs();
        //5.判断参数数组是否有效，无效结束
        if (args==null || args.length==0){
            return;
        }
        //6.获取参数数组下标0的参数对象
        Object obj = args[0];
        //7.准备当前时间和登录人id
        LocalDateTime now = LocalDateTime.now();
        Long empId = BaseContext.getCurrentId();
        //8.判断@AutoFill注解对象的值如果为OperationType.Insert，填充4个字段
        try {
            if (operationType == OperationType.INSERT){
                //已知条件:   对象obj  方法名字符串   利用反射原理来实现
                //获取set封装数据方法语法,obj.getClass().getMethod("方法名字符串",参数类型字节码)
                Method createUserMethod = obj.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method updateUserMethod = obj.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                Method createTimeMethod = obj.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method updateTimeMethod = obj.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);

                //执行方法反射语法:method.invoke(方法所属的对象,数据值
                updateUserMethod.invoke(obj,empId);
                createUserMethod.invoke(obj,empId);
                updateTimeMethod.invoke(obj,now);
                createTimeMethod.invoke(obj,now);
            }else {
                //9.否则，填充2个字段
                Method updateUserMethod = obj.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                Method updateTimeMethod = obj.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                updateUserMethod.invoke(obj,empId);
                updateTimeMethod.invoke(obj,now);
            }
        } catch (Exception e) {
            //e.printStackTrace();
            log.error("公共字段填充失败:{}",e.getMessage());
        }

    }
}



